package kuzha.code.jvm;

import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.GETSTATIC;

import java.util.ArrayList;
import java.util.List;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

abstract class AbstractPool<T> {
	public final String systemName;
	public final String fieldsPrefix;
	public final String typeName;
	protected final List<T> objs = new ArrayList<T>();
	
	public AbstractPool(String fieldsPrefix, String systemName, String typeName) {
		this.fieldsPrefix = fieldsPrefix;
		this.systemName = systemName;
		this.typeName = typeName;
	}
	
	public void emitConstantsFields(ClassWriter cw) {
		for(int i = objs.size() - 1; 0 <= i; i--) {
			emitField(cw, getConstFieldName(i));
		}
	}
	protected void emitField(ClassWriter cw, String fieldname) {
		cw.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL, fieldname, typeName, null, null);
	}
	
	public void emitInitializeConstants(MethodVisitor mv) {
		for(int i = objs.size() - 1; 0 <= i; i--) {
			emitInitializeConstant(mv, getConstFieldName(i), objs.get(i));
		}
	}
	protected abstract void emitInitializeConstant(MethodVisitor mv, String fieldname, T obj);
	
	public void emitLoadConstants(MethodVisitor mv, T obj) {
		mv.visitFieldInsn(GETSTATIC, systemName, addConstants(obj), typeName);
	}
	
	protected String getConstFieldName(int index) {
		return fieldsPrefix + index;
	}
	
	protected String addConstants(T obj) {
		int index = objs.indexOf(obj);
		if (index < 0) {
			index = objs.size();
			objs.add(obj);
		}
		return getConstFieldName(index);
	}
}
